﻿
using System;

namespace DataStructures.List
{
    public abstract class AbstractList<E> : Queue.AbstractQueue<E>, IList<E>
    {
        public abstract bool setByIndex(int index, E element);
        public abstract bool insertByIndex(int index, E element);
        public abstract E getByIndex(int index);
        public abstract E removeByIndex(int index);

        protected virtual void checkEmpty()
        {
            if (Size == 0)
            {
                throw new EmptyListException(
                    "Trying to access elements from an empty list",
                    new IndexOutOfRangeException("The size of the list is not larger than the index"));
            }
        }

        protected virtual void checkIndexMin(int index)
        {
            if (index < 0) throw new ArgumentOutOfRangeException("index", "The index cannot be negative");
        }
        protected virtual void checkIndexMax(int index)
        {
            if (index >= Size)
            {
                throw new IndexOutOfRangeException(
                    "The size of the list is not larger than the index");
            }
        }

        public virtual E[] toArray()
        {
            E[] array = new E[Size];
            int i = Size;
            while (i > 0)
            {
                array[i] = getByIndex(--i);
            }
            return array;
        }

        public virtual int indexOf(E element)
        {
            for (int i = 0; i < Size; ++i)
            {
                if (getByIndex(i).Equals(element)) return i;
            }
            return -1;
        }
        public virtual int lastIndexOf(E element)
        {
            for (int i = Size; i > 0;)
            {
                if (getByIndex(--i).Equals(element)) return i;
            }
            return -1;
        }

        public virtual bool insertBeforeFirst(E element)
        {
            return insertByIndex(0, element);
        }

        public virtual bool insertAfterLast(E element)
        {
            return insertByIndex(Size, element);
        }

        public virtual bool setFirst(E element)
        {
            return setByIndex(0, element);
        }

        public virtual bool setLast(E element)
        {
            return setByIndex(Size - 1, element);
        }

        public override E getFirst()
        {
            return getByIndex(0);
        }

        public override E getLast()
        {
            return getByIndex(Size - 1);
        }

        public virtual E removeFirst()
        {
            return removeByIndex(0);
        }

        public virtual E removeLast()
        {
            return removeByIndex(Size - 1);
        }

        public virtual bool push(E element)
        {
            return insertBeforeFirst(element);
        }
        public virtual E pop()
        {
            return removeFirst();
        }

        public virtual E getTop()
        {
            return getFirst();
        }
        public virtual bool setTop(E element)
        {
            return setFirst(element);
        }

        public override bool enqueue(E element)
        {
            return insertAfterLast(element);
        }

        public override E dequeue()
        {
            return removeFirst();
        }

        public override bool hasElement(E element)
        {
            return lastIndexOf(element) >= 0;
        }

        public override bool removeElement(E element)
        {
            for (int i = 0; i < Size; ++i)
            {
                if (getByIndex(i).Equals(element))
                {
                    removeByIndex(i);
                    return true;
                }
            }
            return false;
        }
    }
}
